package com.mk.web.structure.sort.insertion;

import java.util.Arrays;

/**
 * 希尔排序
 * 也称 递减增量排序算法，1959年Shell发明。是插入排序的一种高速而<b>稳定的</b>改进版本。
 *
 * Created by WangChen on 2018/12/18 22:53.
 */
public class ShellSort {

    public static void main(String [] args){
        int[] array = {6, 5, 7, 3, 8, 2, 4, 1, 9, 13, 11};// 1.要排序的数组
        String[][] s = new String[1][1];
        s[0][0] = "s";
        // 执行插入排序
        shellSort3(array);
        // 输出结果
        System.out.println(Arrays.toString(array));
    }

    /**
     * 非稳定的
     * T(n) = O(n²)
     * S(n) = O(1)
     * 时间复杂度 平均时间 O(nlogn)
     * 基本思想：先取一个小于n的整数d1作为第一个增量，把文件的全部记录分组。
     * 所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序；
     * 然后，取第二个增量d2<d1重复上述的分组和排序，直至所取的增量dt=1(dt<dt-1…<d2<d1)，
     * 即所有记录放在同一组中进行直接插入排序为止。
     *
     *一般来说最简单的步长取值是初次取数组长度的一半为增量，之后每次再减半，直到增量为1。
     * 最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,...)，
     * 2^(i+2) * (2^(i+2) - 3) +1
     */
    private static void shellSort(int[] arr) {
        int j;
        for(int gap = arr.length/2; gap>0; gap /= 2){
            //定义一个增长序列，即分割数组的增量,d1=N/2   dk=(d(k-1))/2
            for(int i = gap; i < arr.length; i++){
                int tmp = arr[i];
                for(j = i; j>=gap && tmp<arr[j-gap]; j -= gap){
                    //将相距为Dk的元素进行排序
                    arr[j] = arr[j-gap];
                }
                arr[j] = tmp;
            }
        }
    }

    public static void shellSort2(int[] arr){
        int gap = arr.length / 2;
        for (; gap > 0; gap /= 2) {      //不断缩小gap，直到1为止
            for (int j = 0; (j+gap) < arr.length; j++){     //使用当前gap进行组内插入排序
                for(int k = 0; (k+gap)< arr.length; k += gap){
                    if(arr[k] > arr[k+gap]) {
                        int temp = arr[k+gap];      //交换操作
                        arr[k+gap] = arr[k];
                        arr[k] = temp;
//                        System.out.println("    Sorting:  " + Arrays.toString(arr));
                    }
                }
            }
        }
    }

    /**
     * 自己写的
     */
    public static void shellSort3(int[] arr){
//        int gap = arr.length / 2;
        for (int gap = arr.length / 2; gap > 0; gap /= 2){
            // 下面即直接插入排序
            for (int i = 0; i  < gap; i++){
                for (int j = i; j + gap < arr.length; j += gap){
                    if (arr[j] > arr[j+gap]){
                        int tmp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = tmp;
                    }
                }
                /*下面这种写法是错的, 因为只比了一次 j-gap 与 j, j+n*gap都没有参与比较
                for (int j = i+gap; j > i; j -= gap){
                    if (arr[j-gap] <= arr[j]){
                        break;
                    }
                    int tmp = arr[j-gap];
                    arr[j-gap] = arr[j];
                    arr[j] = tmp;
                }*/
            }
        }
    }

    private static void group1(int[] arr){
        // 确定步长
        int gap = arr.length >> 1;
        Integer[][] gapArr = null;
        for (; gap > 0; gap = gap >> 1){
            // 并分组
            int groupNum = arr.length/gap;
            gapArr = new Integer[groupNum][gap];
            for (int k = 0; k < groupNum; k++){
                for (int m = 0; m < gap; m++){
                    int tt = arr[m + k*gap];
                    gapArr[k][m] = tt;
                }
                System.out.println(Arrays.toString(gapArr[k]));
            }
        }
    }

    private static void group2(int[] arr){
        // 确定步长, 并分组
        int gap = arr.length / 3;
        Integer[][] gapArr = null;
        for (; gap > 0; gap = gap / 3){
            int groupNum = arr.length/gap;
            gapArr = new Integer[gap][groupNum];
            for (int m = 0; m < gap; m++){
                for (int k = 0; k < groupNum; k++){
                    int tt = arr[m + k*gap];
                    gapArr[m][k] = tt;
                }
                System.out.println(Arrays.toString(gapArr[m]));
            }
        }
    }
}
